using System;
using System.Data;
using System.Data.Common;
using FireStarter.Base;
using Firestarter.DB.Schema.BO;
using Firestarter.DB.Schema.Logic;
using Habanero.Base;
using Habanero.BO;
using Habanero.DB;
using Habanero.Testability;
using NUnit.Framework;
using Rhino.Mocks;
using Rhino.Mocks.Constraints;

namespace Firestarter.DB.Schema.Test.Logic
{
    // ReSharper disable InconsistentNaming
    [TestFixture]
    public class TestSchemaMerger
    {
        // private static readonly string _testDatabaseName = new TestUtilsDBStub().TestDatabaseName;//  "SchemaInfo_Test" + GetRandomString();
        private TestUtilsDB_MySql _testUtilsDBMySql;
        private TestUtilsDB_Access _testUtilsDBAccess;
        private TestUtilsDB_SqlServer _testUtilsDbSqlServer;

        [TestFixtureSetUp]
        public void SetupFixture()
        {
            TestUtilsDBSchema.LoadClassDefs();
            BORegistry.DataAccessor = new DataAccessorInMemory();
        }

        [SetUp]
        public void SetupTest()
        {
            // TestUtilsDBSchema.CreateTestDatabase(_testDatabaseName);
            _testUtilsDBMySql = new TestUtilsDB_MySql();
            _testUtilsDBMySql.CreateTestDatabase();
            _testUtilsDBMySql.CreateSchemaTestPack();
        }


        [TearDown]
        public virtual void TearDown()
        {
            if (_testUtilsDBMySql != null)
            {
                _testUtilsDBMySql.DropDatabase();
            }
            if (_testUtilsDBAccess != null)
            {
                _testUtilsDBAccess.DropDatabase();
            }
            if (_testUtilsDbSqlServer != null)
            {
                _testUtilsDbSqlServer.DropDatabase();
            }
        }


        private string TestDatabaseName()
        {
            return _testUtilsDBMySql.TestDatabaseName;
        }

        private static string GetRandomString()
        {
            return RandomValueGen.GetRandomString();
        }

        private DatabaseConnectionInfo CreateTestDatabaseConnectionInfo()
        {
/*            DatabaseConfig databaseConfig = new DatabaseConfig
                ("Mysql", "localhost", _testDatabaseName, "root", "root", null);*/
            return DatabaseConnectionInfo.CreateFromDatabaseConfig(_testUtilsDBMySql.CreateTestDatabaseConfig());
        }

        private DatabaseConnectionInfo CreateTestDatabaseConnectionInfoSqlServer(out IDbConnection dbConnection)
        {
            DatabaseConfig databaseConfig = new DatabaseConfig
                ("SqlServer", "localhost", TestDatabaseName(), "sa", "sa", null);
            //DatabaseConfig databaseConfig = _testUtilsDBMySql.CreateTestDatabaseConfig();
            dbConnection = databaseConfig.GetConnection();
            return DatabaseConnectionInfo.CreateFromDatabaseConfig(databaseConfig);
        }


        [Test]
        public void Test_Construct()
        {
            //---------------Set up test pack-------------------
            ReverseEngineering reverseEngineering = GetReverseEngineering();
            //-------------Assert Preconditions -------------

            //---------------Execute Test ----------------------
            SchemaMerger schemaMerger = new SchemaMerger(reverseEngineering);
            //---------------Test Result -----------------------
            Assert.AreSame(reverseEngineering, schemaMerger.ReverseEngineering);
        }

        [Test]
        public void Test_ReverseEngineering_SetStoredDB()
        {
            //---------------Set up test pack-------------------
            ReverseEngineering reverseEngineering = new ReverseEngineering();
            DBDatabase database = new DBDatabase();
            //-------------Assert Preconditions -------------
            SingleRelationship<DBDatabase> storedDBRelationship =
                (SingleRelationship<DBDatabase>) reverseEngineering.Relationships["StoredDatabase"];
            Assert.IsTrue(storedDBRelationship.OwningBOHasForeignKey);
            Assert.IsNull(reverseEngineering.StoredDatabase);
            Assert.IsNull(reverseEngineering.StoredDatabaseID);
            Assert.IsNull(reverseEngineering.StoredDatabase);
            Assert.IsNull(database.StoredByReverseEngineering);
            //---------------Execute Test ----------------------
            reverseEngineering.StoredDatabase = database;
            //---------------Test Result -----------------------
            IDBDatabase storedDBDatabase = reverseEngineering.StoredDatabase;
            Assert.IsNotNull(storedDBDatabase);
            Assert.AreSame(database, storedDBDatabase);
        }

        [Test]
        public void Test_ReverseEngineering_SetStoredDBID()
        {
            //---------------Set up test pack-------------------
            ReverseEngineering reverseEngineering = GetReverseEngineering();
            DBDatabase database = new DBDatabase();
            //-------------Assert Preconditions -------------
            Assert.IsNull(reverseEngineering.StoredDatabase);
            //---------------Execute Test ----------------------
            reverseEngineering.StoredDatabaseID = database.DatabaseID;
            //---------------Test Result -----------------------
            IDBDatabase storedDBDatabase = reverseEngineering.StoredDatabase;
            Assert.IsNotNull(storedDBDatabase);
            Assert.AreSame(database, storedDBDatabase);
        }

        [Test]
        public void Test_RunMerge_FirstTime()
        {
            //---------------Set up test pack-------------------
            ReverseEngineering reverseEngineering = GetReverseEngineering();
            SchemaMerger schemaMerger = new SchemaMerger(reverseEngineering);
            reverseEngineering.DatabaseConnectionInfo = CreateTestDatabaseConnectionInfo();
            //-------------Assert Preconditions -------------
            Assert.IsNull(reverseEngineering.StoredDatabase);
            //---------------Execute Test ----------------------
            schemaMerger.RunMerge();
            //---------------Test Result -----------------------
            DBDatabase storedDBDatabase = (DBDatabase) reverseEngineering.StoredDatabase;
            Assert.IsNotNull(storedDBDatabase);
            Assert.AreEqual(7, storedDBDatabase.Tables.Count, "Incorrect number of tables");
        }

        private static ReverseEngineering GetReverseEngineering()
        {
            var reverseEngineering = new ReverseEngineering
                                     {
                                         ReverseEngineeringSettings = new ReverseEngineeringSettings()
                                     };
            return reverseEngineering;
        }

        [Test]
        public void Test_RunMerge_UpdateModel()
        {
            //---------------Set up test pack-------------------
            ReverseEngineering reverseEngineering = GetReverseEngineering();
            SchemaMerger schemaMerger = new SchemaMerger(reverseEngineering);
            reverseEngineering.DatabaseConnectionInfo = CreateTestDatabaseConnectionInfo();
            schemaMerger.RunMerge();
            DBDatabase storedDBDatabase = (DBDatabase) reverseEngineering.StoredDatabase;
            const string childTableName = "childtable";
            DBTable childDBTable = storedDBDatabase.GetTable(childTableName);
            storedDBDatabase.Tables.Remove(childDBTable);
            //-------------Assert Preconditions -------------
            Assert.AreEqual(6, storedDBDatabase.Tables.Count, "Incorrect number of tables");
            Assert.IsNull(storedDBDatabase.GetTable(childTableName));
            //---------------Execute Test ----------------------
            schemaMerger.RunMerge();
            //---------------Test Result -----------------------
            Assert.AreEqual(7, storedDBDatabase.Tables.Count, "Incorrect number of tables");
            childDBTable = storedDBDatabase.GetTable(childTableName);
            Assert.IsNotNull(childDBTable);
            Assert.AreEqual(ChangeType.Add, childDBTable.PendingChangeType);
        }

        [Test]
        public void Test_RunMerge_FirstTime_SQLServer()
        {
            //---------------Set up test pack-------------------
            _testUtilsDbSqlServer = new TestUtilsDB_SqlServer();

            //testUtilsDbSqlServer.DropDatabase(dbConnection);
            _testUtilsDbSqlServer.CreateTestDatabase();
            _testUtilsDbSqlServer.CreateSchemaTestPack();
            ReverseEngineering reverseEngineering = GetReverseEngineering();
            SchemaMerger schemaMerger = new SchemaMerger(reverseEngineering);

            var databaseConnectionInfo =
                DatabaseConnectionInfo.CreateFromDatabaseConfig(_testUtilsDbSqlServer.CreateTestDatabaseConfig());
            reverseEngineering.DatabaseConnectionInfo = databaseConnectionInfo;
            //-------------Assert Preconditions -------------
            Assert.IsNull(reverseEngineering.StoredDatabase);
            //---------------Execute Test ----------------------
            schemaMerger.RunMerge();
            //---------------Test Result -----------------------

            IDBDatabase storedDBDatabase = reverseEngineering.StoredDatabase;
            Assert.IsNotNull(storedDBDatabase);
            Assert.AreEqual(7, storedDBDatabase.Tables.Count, "Incorrect number of tables");
        }


        //[Test]
        //public void Test_RunMerge_UpdateModel_SQLServer()
        //{
        //    //---------------Set up test pack-------------------
        //    TestUtilsDB_SqlServer testUtilsDbSqlServer = new TestUtilsDB_SqlServer();
        //    testUtilsDbSqlServer.CreateSchemaTestPack();
        //    ReverseEngineering reverseEngineering = new ReverseEngineering();
        //    SchemaMerger schemaMerger = new SchemaMerger(reverseEngineering);
        //    IDbConnection dbConnection;
        //    reverseEngineering.DatabaseConnectionInfo = CreateTestDatabaseConnectionInfo_SQLServer(out dbConnection);
        //    try
        //    {
        //        schemaMerger.RunMerge();
        //        DBDatabase storedDBDatabase = reverseEngineering.StoredDatabase;
        //        const string childTableName = "childtable";
        //        DBTable childDBTable = storedDBDatabase.GetTable(childTableName);
        //        storedDBDatabase.Tables.Remove(childDBTable);
        //        //-------------Assert Preconditions -------------
        //        Assert.AreEqual(6, storedDBDatabase.Tables.Count, "Incorrect number of tables");
        //        Assert.IsNull(storedDBDatabase.GetTable(childTableName));
        //        //---------------Execute Test ----------------------
        //        schemaMerger.RunMerge();
        //        //---------------Test Result -----------------------
        //        Assert.AreEqual(7, storedDBDatabase.Tables.Count, "Incorrect number of tables");
        //        childDBTable = storedDBDatabase.GetTable(childTableName);
        //        Assert.IsNotNull(childDBTable);
        //        Assert.AreEqual(ChangeType.Add, childDBTable.PendingChangeType);
        //    }
        //    finally``
        //    {
        //        testUtilsDbSqlServer.ClearDatabase(dbConnection);
        //    }
        //}

        [Test]
        public void Test_GetDBDataTypeMapping_GetsMySQL()
        {
            //---------------Set up test pack-------------------
            SchemaMergerSpy schemaMerger = new SchemaMergerSpy(null);
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            IDBDataTypeMapping mapping = schemaMerger.CallGetDBDataTypeMapping("mysql");
            //---------------Test Result -----------------------
            Assert.IsNotNull(mapping);
            Assert.IsInstanceOf(typeof (DBDataTypeMappingMySQL), mapping);
        }

        [Test]
        public void Test_GetDBDataTypeMapping_GetsSqlServer()
        {
            //---------------Set up test pack-------------------
            SchemaMergerSpy schemaMerger = new SchemaMergerSpy(null);
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            IDBDataTypeMapping mapping = schemaMerger.CallGetDBDataTypeMapping("sqlserver");
            //---------------Test Result -----------------------
            Assert.IsNotNull(mapping);
            Assert.IsInstanceOf(typeof (DBDataTypeMappingSQLServer), mapping);
        }

        [Test]
        public void Test_GetDBDataTypeMapping_GetsAccess()
        {
            //---------------Set up test pack-------------------
            SchemaMergerSpy schemaMerger = new SchemaMergerSpy(null);
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            IDBDataTypeMapping mapping = schemaMerger.CallGetDBDataTypeMapping("access");
            //---------------Test Result -----------------------
            Assert.IsNotNull(mapping);
            Assert.IsInstanceOf(typeof (DBDataTypeMappingAccess), mapping);
        }

        [Test]
        public void Test_GetDBDataTypeMapping_GetsNullIfVendorNotRecognised()
        {
            //---------------Set up test pack-------------------
            SchemaMergerSpy schemaMerger = new SchemaMergerSpy(null);
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            IDBDataTypeMapping mapping = schemaMerger.CallGetDBDataTypeMapping(GetRandomString());
            //---------------Test Result -----------------------
            Assert.IsNull(mapping);
        }

        [Test]
        public void Test_RunMerge_SetsDBDataTypeConverter()
        {
            //---------------Set up test pack-------------------
            ReverseEngineering reverseEngineering = new ReverseEngineering();
            DatabaseConnectionInfo databaseConnectionInfo = new DatabaseConnectionInfo();
            databaseConnectionInfo.Vendor = "mysql";
            reverseEngineering.DatabaseConnectionInfo = databaseConnectionInfo;
            SchemaMergerSpy schemaMerger = new SchemaMergerSpy(reverseEngineering);
            //-------------Assert Preconditions -------------
            Assert.IsInstanceOf(typeof (SchemaMerger), schemaMerger);
            Assert.IsFalse(schemaMerger.MethodHasBeenHit);
            //---------------Execute Test ----------------------
            DatabaseReverseEngineerer databaseReverseEngineerer = schemaMerger.CallGetDatabaseReverseEngineer(null);
            //---------------Test Result -----------------------
            Assert.IsNotNull(databaseReverseEngineerer);
            Assert.IsNotNull(databaseReverseEngineerer.DBTypeConverter);
            Assert.IsNotNull(databaseReverseEngineerer.DBTypeConverter.TypeMappings);
            Assert.IsTrue(schemaMerger.MethodHasBeenHit);
        }

        [Test]
        public void Test_GetDBSchemaExtractor_GetsMySQL()
        {
            //---------------Set up test pack-------------------
            IDbConnection connection = MockRepository.GenerateStub<IDbConnection>();
            SchemaMergerSpy schemaMerger = new SchemaMergerSpy(null);

            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            IDBSchemaExtractor mapping = schemaMerger.CallGetDBSchemaExtractor("mysql", connection);
            //---------------Test Result -----------------------
            Assert.IsNotNull(mapping);
            Assert.IsInstanceOf(typeof (DBSchemaExtractorMySQL), mapping);
        }

        [Test]
        public void Test_GetDBSchemaExtractor_GetsSqlServer()
        {
            //---------------Set up test pack-------------------
            IDbConnection connection = MockRepository.GenerateStub<IDbConnection>();
            SchemaMergerSpy schemaMerger = new SchemaMergerSpy(null);
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            IDBSchemaExtractor mapping = schemaMerger.CallGetDBSchemaExtractor("sqlserver", connection);
            //---------------Test Result -----------------------
            Assert.IsNotNull(mapping);
            Assert.IsInstanceOf(typeof (DBSchemaExtractorSQLServer), mapping);
        }

        [Test]
        public void Test_GetDBSchemaExtractor_GetsAccess()
        {
            //  ---------------Set up test pack-------------------
/*            DbConnection connection = MockRepository.GenerateStub<DbConnection>();
            string[] parameters = null;
            connection.Stub(dbConnection => dbConnection.GetSchema("Tables", parameters)).Return(new DataTable("tableName"));*/
            _testUtilsDBAccess = new TestUtilsDB_Access();
            _testUtilsDBAccess.CreateTestDatabase();
            IDbConnection connection = _testUtilsDBAccess.CreateTestDBConnection();
            using (connection)
            {
                SchemaMergerSpy schemaMerger = new SchemaMergerSpy(null);
                //---------------Assert Precondition----------------

                //---------------Execute Test ----------------------
                IDBSchemaExtractor mapping = schemaMerger.CallGetDBSchemaExtractor("access", connection);
                //---------------Test Result -----------------------
                Assert.IsNotNull(mapping);
                Assert.IsInstanceOf(typeof (DBSchemaExtractor_Access), mapping);
            }
        }

        [Test]
        public void Test_GetDBSchemaExtractor_GetsNullIfVendorNotRecognised()
        {
            //---------------Set up test pack-------------------
            IDbConnection connection = MockRepository.GenerateStub<IDbConnection>();
            SchemaMergerSpy schemaMerger = new SchemaMergerSpy(null);
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            IDBSchemaExtractor mapping = schemaMerger.CallGetDBSchemaExtractor(GetRandomString(), connection);
            //---------------Test Result -----------------------
            Assert.IsNull(mapping);
        }

        private class SchemaMergerSpy : SchemaMerger
        {
            public bool MethodHasBeenHit { get; private set; }

            public SchemaMergerSpy(IReverseEngineering reverseEngineering)
                : base(reverseEngineering)
            {
                MethodHasBeenHit = false;
            }

            public IDBDataTypeMapping CallGetDBDataTypeMapping(string vendorName)
            {
                return GetDBDataTypeMapping(vendorName);
            }

            protected override IDBDataTypeMapping GetDBDataTypeMapping(string vendorName)
            {
                MethodHasBeenHit = true;
                return base.GetDBDataTypeMapping(vendorName);
            }

            public DBSchemaExtractor CallGetDBSchemaExtractor(string vendorName, IDbConnection dbConnection)
            {
                return GetDBSchemaExtractor(vendorName, dbConnection);
            }

            protected override DBSchemaExtractor GetDBSchemaExtractor(string vendorName, IDbConnection dbConnection)
            {
                MethodHasBeenHit = true;
                return base.GetDBSchemaExtractor(vendorName, dbConnection);
            }

            public DatabaseReverseEngineerer CallGetDatabaseReverseEngineer(IDBSchemaExtractor extractor)
            {
                if (extractor == null) extractor = new DBSchemaExtractor_Fake();
                ReverseEngineering.ReverseEngineeringSettings = new ReverseEngineeringSettings();
                return base.GetDatabaseReverseEngineer(extractor);
            }
        }

        private IDbConnection CreateOpenConnection()
        {
            DatabaseConfig databaseConfig = CreateTestDatabaseConnectionInfo().CreateDatabaseConfig();
            IDatabaseConnection databaseConnection = databaseConfig.GetDatabaseConnection();
            IDbConnection connection = databaseConnection.GetConnection();
            if (connection.State == ConnectionState.Closed) connection.Open();
            return connection;
        }
    }

    // ReSharper restore InconsistentNaming
}